package com.example.test.netty;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.example.test.netty.constant.NettyConstants;
import com.example.test.netty.message.MessageEntity;
import com.example.test.netty.utils.ImageBase64Utils;
import com.example.test.netty.utils.MD5Utils;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;

/**
 * @program: test
 * @description: I/O数据读写处理类
 * @author: qiu bo yang
 * @create: 2020-10-13 11:09
 **/
@Slf4j
public class BootNettyChannelInboundHandlerAdapter extends ChannelInboundHandlerAdapter {
    /**
     * 从客户端收到新的数据时，这个方法会在收到消息时被调用
     *
     * @param ctx
     * @param msg
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception, IOException {
        //log.info("芊熠人脸识别通道消息：{}", msg);
        MessageEntity accept = (MessageEntity) msg;
        if (StringUtils.isNotEmpty(accept.getMsgBody())) {
            JSONObject msgBodyJson = null;

            msgBodyJson = JSON.parseObject(accept.getMsgBody());

            if (msgBodyJson != null &&
                    StringUtils.isNotEmpty(msgBodyJson.getString("message"))) {
                //注册
                if (NettyConstants.registerMessage.equals(msgBodyJson.getString("message"))) {
                    doRegisterResponse(accept, ctx, msgBodyJson);
                }
                //登录
                if (NettyConstants.loginMessage.equals(msgBodyJson.getString("message"))) {
                    doLoginResponse(accept, ctx, msgBodyJson);
                }
                //设备上报消息
                if (NettyConstants.snapshotFace.equals(msgBodyJson.getString("message"))) {
                    doSnapshotFaceResponse(accept, ctx, msgBodyJson);
                }
            }
        }
    }

    /**
     * 从客户端收到新的数据、读取完成时调用
     *
     * @param ctx
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws IOException {
        //log.info("channelReadComplete");
        ctx.flush();
    }

    /**
     * 当出现 Throwable 对象才会被调用，即当 Netty 由于 IO 错误或者处理器在处理事件时抛出的异常时
     *
     * @param ctx
     * @param cause
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws IOException {
        cause.printStackTrace();
        ctx.close();//抛出异常，断开与客户端的连接
        log.info("抛出异常，断开与客户端的连接");
    }

    /**
     * 客户端与服务端第一次建立连接时 执行
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception, IOException {
        super.channelActive(ctx);
        ctx.channel().read();
        InetSocketAddress inSocket = (InetSocketAddress) ctx.channel().remoteAddress();
        String clientIp = inSocket.getAddress().getHostAddress();
        //此处不能使用ctx.close()，否则客户端始终无法与服务端建立连接
        log.info("客户端与服务端建立连接ip：{},name：{}:", clientIp, ctx.name());
    }

    /**
     * 客户端与服务端 断连时 执行
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception, IOException {
        super.channelInactive(ctx);
        InetSocketAddress inSocket = (InetSocketAddress) ctx.channel().remoteAddress();
        String clientIp = inSocket.getAddress().getHostAddress();
        ctx.close(); //断开连接时，必须关闭，否则造成资源浪费，并发量很大情况下可能造成宕机
        log.info("客户端与服务端断开连接ip：{}", clientIp);
    }

    /**
     * 服务端当read超时, 会调用这个方法
     *
     * @param ctx
     * @param evt
     * @throws Exception
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception, IOException {
        super.userEventTriggered(ctx, evt);
        InetSocketAddress inSocket = (InetSocketAddress) ctx.channel().remoteAddress();
        String clientIp = inSocket.getAddress().getHostAddress();
        ctx.close();//超时时断开连接
        log.info("超时断开连接ip：{}", clientIp);
    }

    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        log.info("channelRegistered");
    }

    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        log.info("channelUnregistered");
    }

    @Override
    public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
        log.info("channelWritabilityChanged");
    }

    /**
     * 注册回复设备端
     *
     * @param accept 接收设备对象
     * @param ctx
     */
    private void doRegisterResponse(MessageEntity accept, ChannelHandlerContext ctx, JSONObject msgBodyJson) {
        //回复设备端
        MessageEntity messageEntity = new MessageEntity();
        messageEntity.setMagic(accept.getMagic());
        messageEntity.setVersion(accept.getVersion());
        messageEntity.setMsgType(4);
        messageEntity.setDataType(accept.getDataType());
        messageEntity.setResv(accept.getResv());
        messageEntity.setTimestamp(new Long(System.currentTimeMillis() / 1000).intValue());
        messageEntity.setSquence(accept.getSquence());
        JSONObject dataJson = JSON.parseObject(msgBodyJson.get("data").toString());
        if (dataJson != null) {
            if (StringUtils.isNotEmpty(dataJson.getString("serial")) &&
                    StringUtils.isNotEmpty(dataJson.getString("timestamp")) &&
                    StringUtils.isNotEmpty(accept.getMagic()) &&
                    StringUtils.isNotEmpty(dataJson.getString("mac"))) {
                String serial = dataJson.getString("serial");
                String mac = dataJson.getString("mac");
                String timestamp = dataJson.getString("timestamp");
                String str = serial + mac + timestamp + NettyConstants.checkSignMagic;
                String md5Str = MD5Utils.strToMD5(str);
                String sign = dataJson.getString("sign");
                //log.info("serial：{},mac：{},timestamp：{},magic：{},sign：{},md5Str：{}", serial, mac, timestamp, magic, sign, md5Str);
                if (md5Str.equals(sign)) {
                    String uuid = UUID.randomUUID().toString().replaceAll("-", "");
                    String password = "1602600013616";
                    String result = "{\"result\":0,\"desc\":\"Success\",\"data\":{\"uuid\":\"" + uuid + "\",\"passwd\":\"" + password + "\"}}";
                    messageEntity.setMsgBody(result);
                    ctx.write(messageEntity);
                    log.info("设备：{}注册成功：{}", dataJson.getString("serial"), result);
                }
            }
        }
    }

    /**
     * 登录回复设备端
     *
     * @param accept      接收设备对象
     * @param ctx
     * @param msgBodyJson 接收消息json
     */
    private void doLoginResponse(MessageEntity accept, ChannelHandlerContext ctx, JSONObject msgBodyJson) {
        if (StringUtils.isNotEmpty(msgBodyJson.get("data").toString())) {
            MessageEntity messageEntity = new MessageEntity();
            messageEntity.setMagic(NettyConstants.magic);
            messageEntity.setVersion(accept.getVersion());
            messageEntity.setMsgType(4);
            messageEntity.setDataType(accept.getDataType());
            messageEntity.setResv(accept.getResv());
            messageEntity.setTimestamp(new Long(System.currentTimeMillis() / 1000).intValue());
            messageEntity.setSquence(accept.getSquence());
            JSONObject dataJson = JSON.parseObject(msgBodyJson.get("data").toString());
            if (dataJson != null) {
                if (StringUtils.isNotEmpty(dataJson.getString("uuid")) &&
                        StringUtils.isNotEmpty(dataJson.getString("timestamp")) &&
                        StringUtils.isNotEmpty(accept.getMagic()) &&
                        StringUtils.isNotEmpty(dataJson.getString("sign"))) {
                    String uuid = dataJson.getString("uuid");
                    String password = "1602600013616";
                    String timestamp = dataJson.getString("timestamp");
                    String str = uuid + password + timestamp + NettyConstants.checkSignMagic;
                    String md5Str = MD5Utils.strToMD5(str);
                    String sign = dataJson.getString("sign");
                    //log.info("uuid：{},password：{},timestamp：{},magic：{},sign：{},md5Str：{}", uuid, password, timestamp, magic, sign, md5Str);
                    //校验正确
                    if (sign.equals(md5Str)) {
                        String result = "{\"result\":0,\"desc\":\"Success\",\"data\":{}}";
                        messageEntity.setMsgBody(result);
                        log.info("设备：{}登录成功", uuid);
                    } else {
                        //校验不正确
                        String result = "{\"result\":100,\"desc\":\"Unregistered\",\"data\":{}}";
                        messageEntity.setMsgBody(result);
                    }
                    ctx.write(messageEntity);
                } else {
                    //校验不正确
                    String result = "{\"result\":100,\"desc\":\"Unregistered\",\"data\":{}}";
                    messageEntity.setMsgBody(result);
                    ctx.write(messageEntity);
                }
            } else {
                //校验不正确
                String result = "{\"result\":100,\"desc\":\"Unregistered\",\"data\":{}}";
                messageEntity.setMsgBody(result);
                ctx.write(messageEntity);
            }
        }
    }

    /**
     * 设备上报消息
     *
     * @param accept      接收设备对象
     * @param ctx
     * @param msgBodyJson 接收json
     */
    private void doSnapshotFaceResponse(MessageEntity accept, ChannelHandlerContext ctx, JSONObject msgBodyJson) {
        InetSocketAddress inSocket = (InetSocketAddress) ctx.channel().remoteAddress();
        String clientIp = inSocket.getAddress().getHostAddress();
        log.info("接收设备对象：{},ip：{}", msgBodyJson, clientIp);
        if (StringUtils.isNotEmpty(msgBodyJson.get("data").toString())) {
            JSONObject dataJson = JSON.parseObject(msgBodyJson.get("data").toString());
            if (dataJson != null) {
                MessageEntity messageEntity = new MessageEntity();
                messageEntity.setMagic(NettyConstants.magic);
                messageEntity.setVersion(accept.getVersion());
                messageEntity.setMsgType(4);
                messageEntity.setDataType(accept.getDataType());
                messageEntity.setResv(accept.getResv());
                messageEntity.setTimestamp(new Long(System.currentTimeMillis() / 1000).intValue());
                messageEntity.setSquence(accept.getSquence());
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
                //对比结果0 失败(不匹配)，1 成功（匹配、未过期），2：比对成功，授权已经过期3：比对成功，授权未过期但不在通行时段内
                if ("1".equals(dataJson.getString("result")) &&
                        StringUtils.isNotEmpty(dataJson.getString("snapPic"))) {
                    String snapPic = dataJson.getString("snapPic");
                    ImageBase64Utils.base64StringToImage(snapPic);
                    String result = "{\"result\":0,\"desc\":\"Success\",\"data\":{\"snaptime\":\"" + sdf.format(new Date()) + "\",\"opendoor\":1,\"screenDisp\":\"\"}}";
                    messageEntity.setMsgBody(result);
                } else {
                    String result = "{\"result\":0,\"desc\":\"Success\",\"data\":{\"snaptime\":\"" + sdf.format(new Date()) + "\",\"opendoor\":0,\"screenDisp\":\"\"}}";
                    messageEntity.setMsgBody(result);
                }
                ctx.write(messageEntity);
            }
        }
    }
}
